mod account;
mod client;
mod global;
mod rooms;
mod sending;
mod transaction_ids;
mod user;

use axum::async_trait;
use lazy_static::lazy_static;
use ruma::{OwnedServerName, RoomVersionId, ServerName, UserId};
use std::{
    ops::Deref,
    sync::{Arc, RwLock},
};

use crate::Result;
use user::Service as UserService;

#[async_trait]
pub trait Service {
    fn build(args: &Args) -> Arc<Self>;

    async fn worker(self: Arc<Self>) -> Result<()>;

    fn interrupt(&self);

    fn name(&self) -> &str;
}

pub struct AllServices {
    pub client: Arc<client::Service>,
    pub global: Arc<global::Service>,
    pub users: Arc<user::Service>,
    pub account: Arc<account::Service>,
    // pub rooms: Arc<rooms::Service>,
    // pub rooms_state_cache: Arc<rooms::state_cache::Service>,
}

#[derive(Clone)]
pub struct Args {
    server_name: OwnedServerName,
}

lazy_static! {
    pub static ref ARGS: Args = Args::build(ServerName::parse("localhost").unwrap());
}

impl Args {
    pub fn build(server_name: OwnedServerName) -> Self {
        Self { server_name }
    }
}

lazy_static! {
    pub static ref SERVICES: AllServices = {
        let args = ARGS.clone();
        // let global = global::Service::build(&args).unwrap();
        let global = global::Service::new();
        // let users = user::Service::build(&args).unwrap();
        let users = user::Service::new();
        // let account = account::Service::build(&args).unwrap();
        let account = account::Service::new();
        // let rooms_state_cache = rooms::state_cache::Service::build(&args).unwrap();
        let client = client::Service::build(&args).unwrap();
        AllServices {
            global: Arc::new(global),
            users: Arc::new(users),
            account: Arc::new(account),
            client,
        }
    };
}

lazy_static! {
    pub static ref SERVER: Arc<RwLock<Server>> = {
        Arc::new(RwLock::new(Server {
            config: CONFIG.clone(),
            users: user::Service::new(),
            account: account::Service::new(),
            global: global::Service::new(),
            rooms: rooms::Service::build(),
            sending: sending::Service::build(&ARGS),
            transaction_ids: transaction_ids::Service::new(),
        }))
    };
}

pub fn server() -> &'static Server {
    let x = SERVER.read().unwrap();
    let x = x.deref();
    unsafe { std::mem::transmute(x) }
}

pub struct Server {
    pub global: global::Service,
    pub config: Config,
    pub users: UserService,
    pub account: account::Service,
    pub rooms: rooms::Service,
    pub sending: Arc<sending::Service>,
    pub transaction_ids: transaction_ids::Service,
}

lazy_static! {
    pub static ref CONFIG: Config = Config {
        server_name: OwnedServerName::try_from("localhost").unwrap(),
        max_request_size: 1024 * 1024,
        default_room_version: RoomVersionId::V10,
    };
}

#[derive(Debug, Clone)]
pub struct Config {
    pub server_name: OwnedServerName,
    pub max_request_size: u64,
    pub default_room_version: RoomVersionId,
}

impl Config {
    pub fn user_is_local(&self, user_id: &UserId) -> bool {
        user_id.server_name() == self.server_name
    }
    pub fn server_is_ours(&self, server_name: &ServerName) -> bool {
        server_name == &self.server_name
    }
}
